﻿#region Using directives
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.Script.Services;
using System.Web.Security;
using System.Web.Services;

#endregion

namespace ProcolServices {
    /// <summary>
    /// Summary description for UserService
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    [ScriptService]
    public class UserService : WebService {
        private readonly ProcolDataContext db = new ProcolDataContext();

        [WebMethod]
        public void SetUserLanguage(int uid, int newLanguage) {
            try {
                var user = (from u in db.Users
                            where u.UserId == uid
                            select u).Single();

#pragma warning disable 168
                var checkLanguage = (from l in db.Languages
#pragma warning restore 168
                                     where l.LanguageId == newLanguage
                                     select l).Single();

                user.LanguageId = newLanguage;
                db.SubmitChanges();
            } catch (InvalidOperationException) {
            }
        }

        [WebMethod]
        public String GetUserMajor(int uid, int language) {
            try {
                var query = (from m in db.Majors
                             let mId = (from u in db.Users
                                        where u.UserId == uid
                                        select u.MajorId).SingleOrDefault()
                             where m.LanguageId == language && m.MajorId == mId
                             select m.Text).Single();

                return query;
            } catch (InvalidOperationException) {
                return "N/A";
            }
        }

        [WebMethod]
        public int GetUserRole(int uid) {
            try {
                var query = from u in db.Users
                            where u.UserId == uid
                            select u.Role;

                return query.Take(1).ToArray()[0];
            } catch (InvalidOperationException) {
                return -1;
            }
        }

        [WebMethod]
        public void SetUserMajor(int uid, String newMajor) {
            try {
                var user = (from u in db.Users
                            where u.UserId == uid
                            select u).Single();

#pragma warning disable 168
                var checkMajor = (from m in db.Majors
#pragma warning restore 168
                                  where m.MajorId == newMajor
                                  select m).First();

                user.MajorId = newMajor.ToUpper();
                db.SubmitChanges();
            } catch (InvalidOperationException) {
            }
        }

        [WebMethod]
        public int GetUserLanguage(int uid) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select u.LanguageId).Single();

                return query;
            } catch (InvalidOperationException) {
                return 1;
            }
        }

        [WebMethod]
        public String GetUserUsername(int uid) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select u.Username).Single();

                return query;
            } catch (InvalidOperationException) {
                return "NULL";
            }
        }

        [WebMethod]
        public int GetUserIdFromUsername(String username) {
            try {
                var query = (from u in db.Users
                             where u.Username == username
                             select u.UserId).Single();

                return query;
            } catch (InvalidOperationException) {
                return -1;
            }
        }

        [WebMethod]
        public String GetUserName(int uid) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select String.Join(" ", new[] {u.Name, u.LastnameD, u.LastnameM})).Single();

                return query;
            } catch (InvalidOperationException) {
                return "NULL";
            }
        }

        [WebMethod]
        public byte GetUserGender(int uid) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select u.Gender).Single();

                return query;
            } catch (InvalidOperationException) {
                return 0;
            }
        }

        [WebMethod]
        public DateTime GetUserDOB(int uid) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select u.DateOfBirth).Single();

                return query;
            } catch (InvalidOperationException) {
                return DateTime.Now;
            }
        }

        [WebMethod]
        public void SetUserDOB(int uid, DateTime dob) {
            try {
                var user = (from u in db.Users
                            where u.UserId == uid
                            select u).Single();
                user.DateOfBirth = dob;
                db.SubmitChanges();
            } catch (InvalidOperationException) {
            }
        }

        [WebMethod]
        public String GetUserMail(int uid) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select u.Email).Single();

                return query;
            } catch (InvalidOperationException) {
                return "NULL";
            }
        }

        [WebMethod]
        public String GetUserAvatar(int uid) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select u.Avatar).Single();

                return query;
            } catch (InvalidOperationException) {
                return "NULL";
            }
        }

        [WebMethod]
        public String GetUserPhone(int uid) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select u.Telephone).Single();

                return query;
            } catch (InvalidOperationException) {
                return "NULL";
            }
        }

        [WebMethod]
        public String GetLastIp(int uid) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select u.LastIP).Single();

                return query;
            } catch (InvalidOperationException) {
                return "NULL";
            }
        }

        [WebMethod]
        public void SetLastIp(int uid, String ip) {
            try {
                var query = (from u in db.Users
                             where u.UserId == uid
                             select u).Single();

                query.LastIP = !IsPrivateAddress(ip) ? ip : "189.216.76.66";

                db.SubmitChanges();
            } catch (InvalidOperationException) {
            }
        }

        private static bool IsPrivateAddress(string ip) {
            return ip.StartsWith("127.") || ip.StartsWith("10.") || ip.StartsWith("192.") || ip.StartsWith("172.");
        }

        [WebMethod]
        public int GetNumberOfTeams(int uid) {
            var query = (from u in db.UserTeams
                         where u.UserId == uid
                         select u).Count();

            return query;
        }

        [WebMethod]
        public int[] GetListOfTeachers()
        {
            var query = from u in db.Users
                         where u.Role == 1
                         select u.UserId;

            return query.ToArray();
        }

        [WebMethod]
        public IntString[] GetTeams(int uid) {
            var list = new List<IntString>();
            var query = from u in db.UserTeams
                        join t in db.Teams on u.TeamId equals t.TeamId
                        where u.UserId == uid
                        select new {t.TeamId, t.Name};

            foreach (var q in query)
                list.Add(new IntString {Int = q.TeamId, Str = q.Name});

            return list.ToArray();
        }

        [WebMethod]
        public IntString[] GetRoles(int uid) {
            var list = new List<IntString>();
            var query = from u in db.UserTeams
                        join t in db.Teams on u.TeamId equals t.TeamId
                        join r in db.TeamRoles on u.RoleId equals r.RoleId
                        where u.UserId == uid
                        select new {r.RoleId, r.Name};

            foreach (var q in query)
                list.Add(new IntString {Int = q.RoleId, Str = q.Name});

            return list.ToArray();
        }

        [WebMethod]
        public int GetNumberOfProjects(int uid) {
            var query = (from u in db.UserTeams
                         where u.UserId == uid
                         select u).Count();

            return query;
        }

        [WebMethod]
        public IntString[] GetProjects(int uid) {
            var list = new List<IntString>();
            var query = from u in db.UserTeams
                        join t in db.Teams on u.TeamId equals t.TeamId
                        join p in db.Projects on t.ProjectId equals p.ProjectId
                        where u.UserId == uid
                        select new {p.ProjectId, p.Name};

            foreach (var q in query)
                list.Add(new IntString {Int = q.ProjectId, Str = q.Name});

            return list.ToArray();
        }

        [WebMethod]
        public String[] GetUserInterests(int uid, int langId) {
            return (from u in db.Users
                    let userInterests = (from i in db.UserInterests
                                         where i.UserId == uid
                                         select i.InterestId)
                    let interestNames = (from n in db.InterestNames
                                         where userInterests.Contains(n.InterestId) && n.LanguageId == langId
                                         select n.Name).ToArray()
                    select interestNames).First();
        }

        [WebMethod]
        public bool isPartOfProject(int uid, int projectId)
        {
            if ((from i in db.UserTeams
                 join t in db.Teams on i.TeamId equals t.TeamId
                 where i.UserId == uid && t.ProjectId == projectId
                 select i).ToArray().Length == 0)
                return false;
            else
                return true;
        }

        [WebMethod]
        public bool isTutorOfProject(int uid, int projectId)
        {
            if ((from i in db.Projects
                    where i.ProjectId == projectId && i.TutorId == uid
                    select i).ToArray().Length == 0)
                return false;
            else
                return true;
        }

        [WebMethod]
        /// <summary>
        /// Adds a new user to the database.
        /// Return values:
        ///  -1 - User/email already exists
        ///  -2 - Missing field(s)
        /// Anything else - new user's UID
        /// </summary>
        public int AddNewUser(String user, String pass, String name, byte gender, DateTime dateOfBirth, String lastnameD,
                              String lastnameM, String school, String email, String major, String language) {
            var values = new object[] {user, pass, name, gender, dateOfBirth, lastnameD, lastnameM, school, email, major, language};

            // Check for null/default values (x < 0 or empty string -> "")
            foreach (var value in values) {
                String str = value.ToString();
                int tryParse;

                if (Int32.TryParse(str, out tryParse)) {
                    if (tryParse < 0)
                        return -2;
                } else if (String.IsNullOrEmpty(str))
                    return -2;
            }

            try {
#pragma warning disable 168
                User query = (from u in db.Users
#pragma warning restore 168
                              where u.Username == user || u.Email == email
                              select u).Single();

                return -1;
            } catch (InvalidOperationException) {
                // Yay, no user/email registered!

                int langId;
                int schoolId;

                if (Int32.TryParse(language, out langId) && Int32.TryParse(school, out schoolId)) {
                    var newUser = new User {
                            Username = user,
                            Name = name,
                            Password = FormsAuthentication.HashPasswordForStoringInConfigFile(pass, "SHA1"),
                            Gender = gender,
                            DateOfBirth = dateOfBirth,
                            LastnameD = lastnameD,
                            LastnameM = lastnameM,
                            SchoolId = schoolId,
                            Email = email,
                            MajorId = major,
                            LanguageId = langId,
                            Telephone = "",
                            Role = 0,
                            Avatar = ""
                    };

                    db.Users.InsertOnSubmit(newUser);
                    db.SubmitChanges();

                    return newUser.UserId;
                }

                return -2;
            }
        }

        #region Nested type: IntString
        public class IntString {
            public int Int { get; set; }
            public String Str { get; set; }
        }
        #endregion
    }
}